package jfprincesstests.structures.negotiation;

import java.util.Arrays;

import jfprincesstests.fakes.FakeActivationCmd;
import jfprincesstests.fakes.FakeActivator;
import jfprincesstests.fakes.FakeCmd;
import jfprincesstests.fakes.FakeCondition;
import jfprincesstests.fakes.FakeForCmd;
import jfprincesstests.fakes.FakeUtilityAgent;
import jfuzzyprincepless.agents.INegotiatingAgent;
import jfuzzyprincepless.zarchive.algorithm.structure.ChainCommand;
import jfuzzyprincepless.zarchive.algorithm.structure.ICommandConstruction;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.NegotiationForAbsEach;
import jfuzzyprincepless.zarchive.algorithm.structure.forcnstr.fuzzyfor.NegotiationFuzzyFor;
import jfuzzyprincepless.zarchive.algorithm.structure.ifcnstr.NegotiationIfConstruction;
import jfuzzyprincepless.negotiation.*;
import jfuzzyprincepless.zarchive.variables.FuzzySet;
import jfuzzyprincepless.zarchive.variables.IFuzzySet;
import jfuzzyprincepless.zarchive.negotiation.protocols.*;
import jfuzzyprincepless.zarchive.negotiation.protocols.utilityfuncprotocol.UtilityFunctionBasedProtocol;
import jfuzzyprincepless.zarchive.negotiation.protocols.utilityfuncprotocol.UtilityFuncNegotiation;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.ExecuteIfNegotiation;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.ExecuteForNegotiation;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultExecForProtocol;
import jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.DefaultExecIfProtocol;
import jfuzzyprincepless.zarchive.negotiation.ZArchNegotiationUtils;
import laboratoryQ.junit.ignore.patch.BaseTestCase;

public class TestNegotiationProtocolSystem
	extends BaseTestCase
{
	public void testNegotiationProtocol() {
		StringBuilder sb = new StringBuilder();

		INegotiationProtocol__Q_del protocol =
			ZArchNegotiationUtils.newDefaultNegotiationProtocol();

		// IF
		ICommandConstruction thenCmd =
			new FakeActivationCmd(new FakeCmd(sb, "then"));
		ICommandConstruction elseCmd =
			new FakeActivationCmd(new FakeCmd(sb, "else"));
		FakeCondition condition = new FakeCondition(0.3);
		NegotiationIfConstruction ifConstruction =
			new NegotiationIfConstruction(
				condition,
				Arrays.<ICommandConstruction>asList(thenCmd),
				Arrays.<ICommandConstruction>asList(elseCmd)
			);
		ifConstruction.extractAgentsFromConstructions();
		ICommandConstruction construction = ifConstruction;

		INegotiationContext context =
			new DefaultNegotiationContext();
		INegotiation negotiation =
			protocol.createNegotiation(ifConstruction, context);
		assertNotNull(negotiation);
		negotiation.negotiate();
		negotiation.execute(context);

		assertEquals(
			"else;",
			sb.toString()
		);

		sb.setLength(0);
		condition.setTruthDegree(0.8);
		context = negotiation.negotiate();
		negotiation.execute(context);
		assertEquals(
			"then;",
			sb.toString()
		);

		// FOR
		sb.setLength(0);
		FakeActivationCmd fakeForCmd = new FakeActivationCmd(
			new FakeForCmd<Integer>(sb, "fcmd")
		);
		IFuzzySet<Integer> fSet = new FuzzySet<Integer>();
		fSet.add(1, 0.1);
		fSet.add(2, 0.5);
		fSet.add(3, 0.9);
		// zarchTODO: if and for -- common mean to [] or list
		NegotiationFuzzyFor<Integer> forConstruction =
			new NegotiationFuzzyFor<Integer>(
				fSet,
				new ICommandConstruction[] { fakeForCmd },
				Arrays.<INegotiatingAgent>asList(
					new FakeActivator(fakeForCmd)
				)
			);
		construction = forConstruction;

		context = new DefaultNegotiationContext();
		negotiation =
			protocol.createNegotiation(construction, context);
		assertNotNull(negotiation);
		context = negotiation.negotiate();
		negotiation.execute(context);

		assertEquals(
			"fcmd1mem0.1fcmd2mem0.5fcmd3mem0.9",
			sb.toString()
		);
	}

	/**
	 * FOR -- {@link jfuzzyprincepless.zarchive.negotiation.protocols.utilityfuncprotocol.UtilityFuncNegotiation}
	 * IF -- {@linkplain jfuzzyprincepless.zarchive.negotiation.protocols.propositionnetwork.ExecuteIfNegotiation}
	 */
	public void testSpecifyProtocolForConstructionType() {
		StringBuilder sb = new StringBuilder();
		INegotiationContext context = new DefaultNegotiationContext();

		NegotiationForAbsEach<Integer> forLoop =
			new NegotiationForAbsEach<Integer>(
				Arrays.asList(1, 2, 3),
				new FakeUtilityAgent(
					sb,
					";frst",
					new Object[] { 1, 2, 3 },
					new double[] { 1.0, 3.0 }
				),
				new FakeUtilityAgent(
					sb,
					";scnd",
					new Object[] { 4, 5, 6 },
					new double[] { 5.0, 2.0 }
				)
			);
		forLoop.extractAgentsFromConstructions();

		NegotiationIfConstruction ifConstruction =
			new NegotiationIfConstruction(
				new FakeCondition(0.3),
				Arrays.<ICommandConstruction>asList(
					new FakeActivationCmd(new FakeCmd(sb, ";then"))
				),
				Arrays.<ICommandConstruction>asList(
					new FakeActivationCmd(new FakeCmd(sb, ";else"))
				)
			);
		ifConstruction.extractAgentsFromConstructions();

		INegotiationProtocol__Q_del iprotocol =
			ZArchNegotiationUtils.newDefaultNegotiationProtocol();
		FrontNegotiationProtocol protocol =
			(FrontNegotiationProtocol) iprotocol;

		assertNotNull(protocol.createNegotiation(forLoop, context));
		assertNotNull(protocol.createNegotiation(ifConstruction, context));
		assertIsInstance(
			protocol.createNegotiation(forLoop, context),
			ExecuteForNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(ifConstruction, context),
			ExecuteIfNegotiation.class
		);

		protocol.specifyForType(
			forLoop.getClass(),
			new UtilityFunctionBasedProtocol(
				new DefaultExecForProtocol<Integer>(true)
			)
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop, context),
			UtilityFuncNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(ifConstruction, context),
			ExecuteIfNegotiation.class
		);

		forLoop.setProtocol(protocol);
		ifConstruction.setProtocol(protocol);
		ChainCommand theProgram = new ChainCommand(
			forLoop,
			ifConstruction
		);
		theProgram.execute(context);
		assertEquals(
			";frst[],for1;scnd[],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[1]--i-am-critical,for1;scnd[1],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[4],for1;scnd[4]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[2]--i-am-critical,for1;scnd[2],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[5],for1;scnd[5]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[3]--i-am-critical,for1;scnd[3],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[6],for1;scnd[6]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[1]--i-am-critical,for1;scnd[1],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[4],for1;scnd[4]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[2]--i-am-critical,for1;scnd[2],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";else;",
			sb.toString()
		);

		protocol.specifyForType(
			ifConstruction.getClass(),
			new UtilityFunctionBasedProtocol(
				new DefaultExecIfProtocol()
			)
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop, context),
			UtilityFuncNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(ifConstruction, context),
			UtilityFuncNegotiation.class
		);
	}

	/**
	 * FOR0 -- {@link UtilityFuncNegotiation}
	 * FOR1 -- {@linkplain ExecuteIfNegotiation}
	 */
	public void testSpecifyProtocolForConcreetConstruction() {
		StringBuilder sb = new StringBuilder();
		INegotiationContext context = new DefaultNegotiationContext();

		NegotiationForAbsEach<Integer> forLoop0 =
			new NegotiationForAbsEach<Integer>(
				Arrays.asList(1, 2, 3),
				new FakeUtilityAgent(
					sb,
					";frst",
					new Object[] { 1, 2, 3 },
					new double[] { 1.0, 3.0 }
				),
				new FakeUtilityAgent(
					sb,
					";scnd",
					new Object[] { 4, 5, 6 },
					new double[] { 5.0, 2.0 }
				)
			);
		forLoop0.extractAgentsFromConstructions();

		NegotiationForAbsEach<Integer> forLoop1 =
			new NegotiationForAbsEach<Integer>(
				Arrays.asList(1, 2, 3),
				new FakeUtilityAgent(
					sb,
					";frst",
					new Object[] { 1, 2, 3 },
					new double[] { 1.0, 3.0 },
					true
				)
			);
		forLoop1.extractAgentsFromConstructions();

		INegotiationProtocol__Q_del iprotocol =
			ZArchNegotiationUtils.newDefaultNegotiationProtocol();
		FrontNegotiationProtocol protocol =
			(FrontNegotiationProtocol) iprotocol;

		assertNotNull(protocol.createNegotiation(forLoop0, context));
		assertNotNull(protocol.createNegotiation(forLoop1, context));
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			ExecuteForNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop1, context),
			ExecuteForNegotiation.class
		);

		protocol.specifyForObject(
			forLoop0,
			new UtilityFunctionBasedProtocol(
				new DefaultExecForProtocol<Integer>(true)
			)
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			UtilityFuncNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop1, context),
			ExecuteForNegotiation.class
		);

		protocol.specifyForType(
			forLoop1.getClass(),
			new UtilityFunctionBasedProtocol(
				new DefaultExecForProtocol<Integer>(true)
			)
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			UtilityFuncNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop1, context),
			UtilityFuncNegotiation.class
		);

		protocol.specifyForObject(
			forLoop0,
			new DefaultExecForProtocol<Integer>()
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			ExecuteForNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop1, context),
			UtilityFuncNegotiation.class
		);

		// test execution
		iprotocol = ZArchNegotiationUtils.newDefaultNegotiationProtocol();
		protocol = (FrontNegotiationProtocol) iprotocol;
		protocol.specifyForObject(
			forLoop0,
			new UtilityFunctionBasedProtocol(
				new DefaultExecForProtocol<Integer>(true)
			)
		);

		forLoop0.setProtocol(protocol);
		forLoop1.setProtocol(protocol);
		ChainCommand theProgram = new ChainCommand(
			forLoop0,
			forLoop1
		);
		theProgram.execute(context);
		assertEquals(
			";frst[],for1;scnd[],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[1]--i-am-critical,for1;scnd[1],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[4],for1;scnd[4]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[2]--i-am-critical,for1;scnd[2],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[5],for1;scnd[5]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[3]--i-am-critical,for1;scnd[3],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[6],for1;scnd[6]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[1]--i-am-critical,for1;scnd[1],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[4],for1;scnd[4]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			";frst[2]--i-am-critical,for1;scnd[2],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
			",for1,for2,for3",
			sb.toString()
		);
	}

//	@org.junit.Ignore("fix bug with unregister")
//	public void testSpecifyProtocolForConcreetConstruction__BUG_WITH_SERV_UNREGISTER() {
//		StringBuilder sb = new StringBuilder();
//		INegotiationContext context = new DefaultNegotiationContext();
//
//		NegotiationForAbsEach<Integer> forLoop0 =
//			new NegotiationForAbsEach<Integer>(
//				Arrays.asList(1, 2, 3),
//				new FakeUtilityAgent(
//					sb,
//					";frst",
//					new Object[] { 1, 2, 3 },
//					new double[] { 1.0, 3.0 }
//				),
//				new FakeUtilityAgent(
//					sb,
//					";scnd",
//					new Object[] { 4, 5, 6 },
//					new double[] { 5.0, 2.0 }
//				)
//			);
//		forLoop0.extractAgentsFromConstructions();
//
//		NegotiationForAbsEach<Integer> forLoop1 =
//			new NegotiationForAbsEach<Integer>(
//				Arrays.asList(1, 2, 3),
//				new FakeUtilityAgent(
//					sb,
//					";frst",
//					new Object[] { 1, 2, 3 },
//					new double[] { 1.0, 3.0 }
//				),
//				new FakeUtilityAgent(
//					sb,
//					";scnd",
//					new Object[] { 4, 5, 6 },
//					new double[] { 5.0, 2.0 }
//				)
//			);
//		forLoop1.extractAgentsFromConstructions();
//
//		INegotiationProtocol iprotocol =
//			NegotiationUtils.newDefaultNegotiationProtocol();
//		FrontNegotiationProtocol protocol =
//			(FrontNegotiationProtocol) iprotocol;
//
//		assertNotNull(protocol.createNegotiation(forLoop0, context));
//		assertNotNull(protocol.createNegotiation(forLoop1, context));
//		assertIsInstance(
//			protocol.createNegotiation(forLoop0, context),
//			ExecuteForNegotiation.class
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop1, context),
//			ExecuteForNegotiation.class
//		);
//
//		protocol.specifyForObject(
//			forLoop0,
//			new UtilityFunctionBasedProtocol(
//				new DefaultExecForProtocol<Integer>(true)
//			)
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop0, context),
//			UtilityFuncNegotiation.class
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop1, context),
//			ExecuteForNegotiation.class
//		);
//
//		protocol.specifyForType(
//			forLoop1.getClass(),
//			new UtilityFunctionBasedProtocol(
//				new DefaultExecForProtocol<Integer>(true)
//			)
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop0, context),
//			UtilityFuncNegotiation.class
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop1, context),
//			UtilityFuncNegotiation.class
//		);
//
//		protocol.specifyForObject(
//			forLoop0,
//			new DefaultExecForProtocol<Integer>()
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop0, context),
//			ExecuteForNegotiation.class
//		);
//		assertIsInstance(
//			protocol.createNegotiation(forLoop1, context),
//			UtilityFuncNegotiation.class
//		);
//
//		// test execution
//		iprotocol = NegotiationUtils.newDefaultNegotiationProtocol();
//		protocol = (FrontNegotiationProtocol) iprotocol;
//		protocol.specifyForObject(
//			forLoop0,
//			new UtilityFunctionBasedProtocol(
//				new DefaultExecForProtocol<Integer>(true)
//			)
//		);
//
//		forLoop0.setProtocol(protocol);
//		forLoop1.setProtocol(protocol);
//		ChainCommand theProgram = new ChainCommand(
//			forLoop0,
//			forLoop1
//		);
//		theProgram.execute(context);
//		assertEquals(
//			";frst[],for1;scnd[],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[1]--i-am-critical,for1;scnd[1],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[4],for1;scnd[4]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[2]--i-am-critical,for1;scnd[2],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[5],for1;scnd[5]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[3]--i-am-critical,for1;scnd[3],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[6],for1;scnd[6]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[1]--i-am-critical,for1;scnd[1],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[4],for1;scnd[4]--i-am-critical,for1,for2,for2,for3,for3;frst,stgend;scnd,stgend" +
//			";frst[2]--i-am-critical,for1;scnd[2],for1,for2,for2,for3,for3;frst,stgend;scnd,stgend",
//			sb.toString()
//		);
//	}

	public void testTypeFilter() {
		IFilter filter = new TypeFilter(Integer.class);
		assertTrue(filter.accepts(123));
		assertFalse(filter.accepts(123.456));

		filter = new TypeFilter(Number.class);
		assertFalse(filter.accepts(123));
		assertFalse(filter.accepts(123.456));

		filter = new TypeFilter(Number.class, true);
		assertTrue(filter.accepts(123));
		assertTrue(filter.accepts(123.456));
	}

	public void testChaitNegotiationProtocol() {
		INegotiationContext context =
			new DefaultNegotiationContext();

		ICommandConstruction forLoop0 =
			new NegotiationForAbsEach<Integer>();
		ICommandConstruction forLoop1 =
			new NegotiationForAbsEach<Integer>();
		ICommandConstruction ifConstruction =
			new NegotiationIfConstruction();

		ChaitNegotiationProtocol protocol =
			new ChaitNegotiationProtocol();
		INegotiationProtocol__Q_del iprotocol = protocol;
		assertNotNull(iprotocol);

		assertNull(protocol.createNegotiation(forLoop0, context));
		assertNull(protocol.createNegotiation(forLoop1, context));
		assertNull(protocol.createNegotiation(ifConstruction, context));

		protocol.getStagesList().add(
			new FilteringNegotiationProtocol(
				new TypeFilter(ifConstruction.getClass()),
				new DefaultExecIfProtocol()
			)
		);
		assertNull(protocol.createNegotiation(forLoop0, context));
		assertNull(protocol.createNegotiation(forLoop1, context));
		assertNotNull(protocol.createNegotiation(ifConstruction, context));
		assertIsInstance(
			protocol.createNegotiation(ifConstruction, context),
			ExecuteIfNegotiation.class
		);

		protocol.getStagesList().add(
			new FilteringNegotiationProtocol(
				new ObjFilter(forLoop0),
				new DefaultExecForProtocol<Integer>()
			)
		);
		assertNotNull(protocol.createNegotiation(forLoop0, context));
		assertNull(protocol.createNegotiation(forLoop1, context));
		assertNotNull(protocol.createNegotiation(ifConstruction, context));
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			ExecuteForNegotiation.class
		);

		protocol.getStagesList().add(
			new FilteringNegotiationProtocol(
				new TypeFilter(forLoop1.getClass()),
				new UtilityFunctionBasedProtocol(
					new DefaultExecForProtocol<Integer>()
				)
			)
		);
		assertNotNull(protocol.createNegotiation(forLoop0, context));
		assertNotNull(protocol.createNegotiation(forLoop1, context));
		assertNotNull(protocol.createNegotiation(ifConstruction, context));
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			ExecuteForNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop1, context),
			UtilityFuncNegotiation.class
		);

		protocol.getStagesList().add(
			0,
			new FilteringNegotiationProtocol(
				new TypeFilter(forLoop1.getClass()),
				new UtilityFunctionBasedProtocol(
					new DefaultExecForProtocol<Integer>()
				)
			)
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop0, context),
			UtilityFuncNegotiation.class
		);
		assertIsInstance(
			protocol.createNegotiation(forLoop1, context),
			UtilityFuncNegotiation.class
		);
	}
}
